package com.headmaster.wap.util;

import java.awt.Menu;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.log4j.Logger;

import net.sf.json.JSONObject;

/**
 * 
 * @author suyujun
 * @date 2015年11月12日17:58:03
 * @description  蘑菇公寓公众号对接网站工具类
 *
 */
public class WeixinUtils {
	
	private static final Logger logger = Logger.getLogger(WeixinUtils.class);
	
	/**
	 * @author suyujun
	 * @date 2015年11月13日09:39:53
	 * @description 根据appId和AppSecret获取微信服务器access_token,access_token是公众号的全局唯一票据，
	 * 				公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要
	 * 				保留512个字符空间。access_token的有效期目前为2个小时，需定时刷新，重复获取将导致上次获取的
	 * 				access_token失效。 
	 * 				公众号调用接口并不是无限制的。为了防止公众号的程序错误而引发微信服务器负载异常，默认情况下，每个公众
	 * 				号调用接口都不能超过一定限制，当超过一定限制时，调用对应接口会收到如下错误返回码： 
	 * 				{"errcode":45009,"errmsg":"api freq out of limit"},
	 * 				对获取到的access_token进行缓存，有效期为7200s
	 * @return String access_token
	 * @throws Exception
	 */
	public static final String getAccessToken(String appid,String appsecret) throws Exception{
		String url = WeixinConstant.GET_ACCESS_TOKEN;
		url = url.replace("APPID",appid);
		url = url.replace("APPSECRET", appsecret);
		//正常：{"access_token":"ACCESS_TOKEN","expires_in":7200}
		//异常：{"errcode":40013,"errmsg":"invalid appid"}
		String json = HttpClientUtil.execute(url, new HashMap<String, String>(), "get");
		Map<String,Object> map = JsonUtil.jsonToMap(json);
		if(map.get("errcode") != null){
			logger.info("获取微信access_token 异常，异常消息为：" + map.get("errmsg").toString());
			throw new RuntimeException(json);
		}
		return map.get("access_token").toString();
	}
	
	/**
	 * @author suyujun
	 * @date 2015年11月13日10:31:10
	 * @param accessToken 访问授权码
	 * @description 获取微信服务器ip列表
	 * @return 微信服务器ip列表
	 * @throws Exception
	 */
	public static final String getWeixinServerIps(String accessToken) throws Exception{
		String url = WeixinConstant.GET_WEIXIN_SERVER_IP;
		url = url.replace("ACCESS_TOKEN", accessToken);
		//正常：{"ip_list":["127.0.0.1","127.0.0.1"]}
		//异常：{"errcode":40013,"errmsg":"invalid appid"}
		String json = HttpClientUtil.execute(url, new HashMap<String, String>(), "get");
		Map<String,Object> map = JsonUtil.jsonToMap(json);
		if(map.get("errcode") != null){
			logger.info("获取微信服务器ip列表异常，异常消息为：" + map.get("errmsg").toString());
			throw new RuntimeException(json);
		}
		return map.get("ip_list").toString();
	}
	
	/**
	 * @author suyujun
	 * @date 2015年11月13日15:13:05
	 * @description 通过网页授权code获取网页授权的access_token 和 openid
	 * @param code
	 * @return
	 * @throws Exception
	 */
	public static final Map<String,Object> getAuthOpenIdAndAccessCode(String code,String appid,String appsecrect) throws Exception{
		String url = WeixinConstant.GET_OPENID_URL;
		url = url.replace("APPID", appid);
		url = url.replace("SECRET", appsecrect);
		url = url.replace("CODE", code);
		String json = HttpClientUtil.execute(url, new HashMap<String, String>(), "get");
		logger.info("查看详情授权返回   ：：" + json);
		Map<String,Object> map = JsonUtil.jsonToMap(json);
		//异常：{"errcode":40029,"errmsg":"invalid code"}
		if(map.get("errcode") != null){
			logger.info("获取微信网页授权access_token 异常：" + map.get("errmsg").toString());
			throw new RuntimeException(json);
		}
		return map;
	}
	
	/**
	 * @author suyujun
	 * @date 2015年11月13日15:31:17
	 * @description 拉取微信用户信息
	 * @param access_token 网页授权token
	 * @param openid 微信用户openid
	 * @return map
	 * @throws Exception
	 */
	public static final Map<String,Object> getSnsapiUserinfo(String access_token,String openid) throws Exception{
		String url = WeixinConstant.GET_SNSAPI_USERINFO;
		url = url.replace("ACCESS_TOKEN", access_token);
		url = url.replace("OPENID", openid);
		String json = HttpClientUtil.execute(url, new HashMap<String, String>(), "get");
		logger.info(json);
		Map<String,Object> map = JsonUtil.jsonToMap(json);
		//异常：{"errcode":40003,"errmsg":" invalid openid "}
		if(map.get("errcode") != null){
			logger.info("拉取微信用户信息异常：" + map.get("errmsg").toString());
			throw new RuntimeException(json);
		}
		return map;
	}
	/**
	 * @author suyujun
	 * @date 2015年11月13日11:05:02
	 * @description 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
	 * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
	 * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。 如：X-Forwarded-For：192.168.1.110,
	 * 192.168.1.120, 192.168.1.130, 192.168.1.100 用户真实IP为： 192.168.1.110
	 * @param request
	 * @return 真实IP地址
	 */
	  public static String getIpAddress(HttpServletRequest request) { 
	    String ip = request.getHeader("x-forwarded-for"); 
	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
	      ip = request.getHeader("Proxy-Client-IP"); 
	    } 
	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
	      ip = request.getHeader("WL-Proxy-Client-IP"); 
	    } 
	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
	      ip = request.getHeader("HTTP_CLIENT_IP"); 
	    } 
	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
	      ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 
	    } 
	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
	      ip = request.getRemoteAddr(); 
	    } 
	    return ip; 
	  } 
	  
	  public static final String createSignature(Object...arr){
		  Arrays.sort(arr);
		  String toDigStr = "";
		  for(int i = 0 ; i < arr.length; i++){
			  toDigStr = toDigStr + arr[i];
		  }
		  logger.info("toDigStr=" + toDigStr);
		  
		  String signature = DigestUtils.shaHex(toDigStr);
		  return signature;
	  }
	  
	  /**
	   * @author suyujun
	   * @date 2015年11月25日15:22:36
	   * @description 从微信服务器拉取用户的所有信息
	   * @param access_token 访问授权码
	   * @param openid 微信用户标示
	   * @return map
	   * @throws Exception
	   */
	  public static final Map<String,Object> getBasicUserInfo(String access_token,String openid) throws Exception{
		  String url = WeixinConstant.GET_BASIC_USERINFO;
		  url = url.replace("ACCESS_TOKEN", access_token);
		  url = url.replace("OPENID", openid);
		  String json = HttpClientUtil.execute(url, new HashMap<String, String>(), "get");
		  //乱码处理
//		  json = new String(json.getBytes("gbk"), "utf-8");
		  logger.info(json);
		  Map<String,Object> map = JsonUtil.jsonToMap(json);
		  //异常 {"errcode":40013,"errmsg":"invalid appid"}
		  if(map.get("errcode") != null){
			  logger.info("拉取微信用户基本信息异常：" + map.get("errmsg").toString());
			  throw new RuntimeException(json);
		  }
		  return map;
	  }
	  
	  /**
	   * 生成二维码
	   * @param scene_id 
	   * @param accessToken
	   * @return
	   * @throws Exception
	   */
	  public static String  createQrcode(String scene_id,String accessToken) throws Exception{
	    	String url = WeixinConstant.QRCODE_CREATE_URL.replace("TOKEN", accessToken);
	    	
	    	JSONObject json = new JSONObject();
	    	json.put("expire_seconds", "1800");
	    	json.put("action_name", "QR_SCENE");
	    	JSONObject sceneJson = new JSONObject();
	    	sceneJson.put("scene_id", Long.valueOf(scene_id));
	    	JSONObject actionInfoJson = new JSONObject();
	    	actionInfoJson.put("scene", sceneJson);
	    	json.put("action_info", actionInfoJson);
	    	JSONObject jsonObject = HttpClientUtil.httpRequest(url,"POST",json.toString()); 
	    	logger.info("生成二维码返回json："+jsonObject);
	    	String ticket = "";
	    	if(null != jsonObject){
	    		if(null != jsonObject.get("ticket")){
	    			ticket = (String)jsonObject.get("ticket");
	    		}
	    	}
	    	return ticket;
	    }
	  
	  	// 菜单创建（POST） 限100（次/天）  
	    public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
	    
	    /** 
	     * 创建菜单 
	     *  
	     * @param menu 菜单实例 
	     * @param accessToken 有效的access_token 
	     * @return 0表示成功，其他值表示失败 
	     */  
	    public static int createMenu(Menu menu, String accessToken) {  
	        int result = 0;  
	      
	        // 拼装创建菜单的url  
	        String url = menu_create_url.replace("ACCESS_TOKEN", accessToken);  
	        // 将菜单对象转换成json字符串  
	        String jsonMenu = JSONObject.fromObject(menu).toString();  
	        logger.info("------>>jsonMenu : " + jsonMenu);
	        // 调用接口创建菜单  
	        JSONObject jsonObject = HttpClientUtil.httpRequest(url, "POST", jsonMenu);  
	        logger.info("------>>Create Weixin Menu Callback Result :  " + jsonObject);
	      
	        if (null != jsonObject) {  
	            if (0 != jsonObject.getInt("errcode")) {  
	                result = jsonObject.getInt("errcode");  
	                logger.error("创建菜单失败 errcode:"+jsonObject.getInt("errcode")+" errmsg:"+jsonObject.getString("errmsg"));
	            }  
	        }  
	      
	        return result;  
	    } 
	    
	    /**
	     * 根据mediaId获取微信素材上的inputstream
	     * @param accessToken
	     * @param mediaId
	     * @return
	     */
	    public static InputStream getInputStream(String accessToken,String mediaId) {
	        InputStream is = null;
	        String url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token="
	                + accessToken + "&media_id=" + mediaId;
	        try {
	            URL urlGet = new URL(url);
	            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
	            http.setRequestMethod("GET"); // 必须是get方式请求
	            http.setRequestProperty("Content-Type",
	                    "application/x-www-form-urlencoded");
	            http.setDoOutput(true);
	            http.setDoInput(true);
	            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
	            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
	            http.connect();
	            // 获取文件转化为byte流
	            is = http.getInputStream();
	        } catch (Exception e) {
	        	logger.error("Get file error,url:"+url,e);
	        	logger.error(e.getMessage(),e);
	        }
	        return is;
	    }
	    /**
	     * 下载图片
	     * @param accessToken
	     * @param mediaId
	     * @param path
	     * @throws Exception
	     */
	    public static boolean saveImageToDisk(String accessToken,String mediaId,String path,String fileName) throws Exception {
	    	boolean isSuccess = false;
	        InputStream inputStream = getInputStream(accessToken,mediaId);
	        byte[] data = new byte[1024];
	        int len = 0;
	        FileOutputStream fileOutputStream = null;
	        try {
	        	File file  = new File(path);
	        	if(!file.exists()){
	        		file.mkdirs();
	        	}
	        	File file1 = new File(path+File.separator+fileName);
	        	if(file1.exists()){
	        		file1.createNewFile();
	        	}
	            fileOutputStream = new FileOutputStream(path+File.separator+fileName);
	            while ((len = inputStream.read(data)) != -1) {
	                fileOutputStream.write(data, 0, len);
	            }
	            isSuccess = true;
	        } catch (IOException e) {
	        	logger.error("Save message error,fileName:"+fileName, e);
	        	logger.error(e.getMessage(), e);
	        } finally {
	            if (inputStream != null) {
	                try {
	                    inputStream.close();
	                } catch (IOException e) {
	                	logger.error(e.getMessage(),e);
	                }
	            }
	            if (fileOutputStream != null) {
	                try {
	                    fileOutputStream.close();
	                } catch (IOException e) {
	                	logger.error(e.getMessage(),e);
	                }
	            }
	        }
	        return isSuccess;
	    }
	    
}
